package com.tplhk.redis.lock.jedis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;

public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 尝试获取分布式锁
     * 加锁方法设置了过期时间
     *
     * @param jedis      Redis客户端
     * @param lockKey    锁的key
     * @param requestId  锁的Value,值是个唯一标识，用来标记加锁的线程请求；可以使用UUID.randomUUID().toString()方法生成
     * @param expireTime 过期时间 ms
     * @return 是否获取成功，成功返回true，否则false
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = null;
        try {
//            2.9.3 的写法
//            result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
//            3.3.0 的写法
            result = jedis.set(lockKey, requestId, SetParams.setParams().nx().ex(expireTime));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return LOCK_SUCCESS.equals(result);
    }

    /**
     * 释放分布式锁
     * 解锁方法中使用了lua脚本，具备原子性，解锁时先判断key的value值，
     * 也就是当初加锁保存的requestId是不是和自己线程保存的一致，
     * 一致才说明是自己当初加的锁，方可进行解锁
     *
     * @param jedis     Redis客户端
     * @param lockKey   锁
     * @param requestId 请求标识，锁的Value
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        Object result = null;
        try {
            //使用lua脚本保证原子性
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return RELEASE_SUCCESS.equals(result);
    }

}
